#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <getopt.h>
#include <bpf/libbpf.h>

#include "unrandomize.bpf.h"
#include "unrandomize.bpf.skel.h"

static char* usage = "Usage: [-p pid] [-u uid] [-v verbose] [-d debug libbpf print]";

static bool g_debug = false;
static bool g_verbose = false;

static int libbpf_print(enum libbpf_print_level level, const char *format, va_list args)
{
    if (g_debug)
        return vfprintf(stderr, format, args);
    else
        return 0;
}

static void perf_buffer_sample(void *ctx, int cpu, void *data, __u32 size) {
    struct s_event *e = data;

    printf("%d %d %s \n", e->pid, e->uid, e->comm);
}

static void perf_buffer_lost(void *ctx, int cpu, __u64 cnt) {
    printf("lost %llu events at cpu %d\n", cnt, cpu);
}

static void parse_args(int args, char* argv[], struct s_filter* filter) {
    int opt;

    while ((opt = getopt(args, argv, "dvp:u:h")) != -1) {
        switch(opt) {
            case 'p':
                filter->pid = atoi(optarg);
                break;
            case 'u':
                filter->uid = atoi(optarg);
                break;
            
            case 'd':
                g_debug = true;
                break;
            
            case 'v':
                g_verbose = true;
                break;

            default:
                printf("%s\n", usage);
                exit(-1);
        }
    }

    if (filter->pid >= 0)
        printf("filter pid: %d\n", filter->pid);
    if (filter->uid >= 0)
        printf("filter uid: %lld\n", filter->uid);
}

int main(int args, char* argv[]) {
    int err;

    struct s_filter filter = {-1, -1};
    parse_args(args, argv, &filter);

    libbpf_set_print(libbpf_print);

    struct unrandomize_bpf* bpf_obj = unrandomize_bpf__open_and_load();
    if (!bpf_obj) {
        fprintf(stderr, "failed to open and load bpf object\n");
        return -1;
    }

    bpf_obj->data->filter = filter;

    /** bpf run */
    err = unrandomize_bpf__attach(bpf_obj);
    if (err) {
        fprintf(stderr, "failed to attach bpf object\n");
        return -1;
    }

#ifdef ___perf_buffer_new6
    struct perf_buffer* pb = perf_buffer__new(bpf_map__fd(bpf_obj->maps.events),
                                              128, perf_buffer_sample, perf_buffer_lost, NULL, NULL);
#else
    struct perf_buffer_opts opts = {
    	.lost_cb = perf_buffer_lost,
	    .sample_cb = perf_buffer_sample,
    };

    struct perf_buffer* pb = perf_buffer__new(bpf_map__fd(bpf_obj->maps.events),
                                              128, &opts);
#endif

    err = libbpf_get_error(pb);
	if (err) {
		pb = NULL;
		fprintf(stderr, "failed to open perf buffer: %d\n", err);
		goto cleanup;
	}

	/* Main polling loop */
	while ((err = perf_buffer__poll(pb, 100)) >= 0);
	printf("Error polling perf buffer: %d\n", err);

cleanup:
    perf_buffer__free(pb);
    unrandomize_bpf__destroy(bpf_obj);
    return 0;
}
